home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Atari Mega Archive 2
/
Atari Mega Archive CD - Volume 2.iso
/
minix
/
up1510b.tgz
/
up1510b
/
src
/
fs
/
link.c
< prev
next >
Wrap
C/C++ Source or Header
|
1990-07-23
|
12KB
|
352 lines
/* This file handles the LINK and UNLINK system calls. It also deals with
* deallocating the storage used by a file when the last UNLINK is done to a
* file and the blocks must be returned to the free block pool.
*
* The entry points into this file are
* do_link: perform the LINK system call
* do_unlink: perform the UNLINK and RMDIR system calls
* do_rename: perform the RENAME system call
* truncate: release all the blocks associated with an inode
*/
#include "fs.h"
#include <sys/stat.h>
#include <string.h>
#include <minix/callnr.h>
#include "buf.h"
#include "file.h"
#include "fproc.h"
#include "inode.h"
#include "param.h"
#define SAME 1000
PRIVATE char dot2[NAME_MAX] = "..\0\0\0\0\0\0\0\0\0\0\0";
/*===========================================================================*
* do_link *
*===========================================================================*/
PUBLIC int do_link()
{
/* Perform the link(name1, name2) system call. */
register struct inode *ip, *rip;
register int r;
char string[NAME_MAX];
struct inode *new_ip;
/* See if 'name' (file to be linked) exists. */
if (fetch_name(name1, name1_length, M1) != OK) return(err_code);
if ( (rip = eat_path(user_path)) == NIL_INODE) return(err_code);
/* Check to see if the file has maximum number of links already. */
r = OK;
if ( (rip->i_nlinks & BYTE) == LINK_MAX) r = EMLINK;
/* Only super_user may link to directories. */
if (r == OK)
if ( (rip->i_mode & I_TYPE) == I_DIRECTORY && !super_user) r = EPERM;
/* If error with 'name', return the inode. */
if (r != OK) {
put_inode(rip);
return(r);
}
/* Does the final directory of 'name2' exist? */
if (fetch_name(name2, name2_length, M1) != OK) {
put_inode(rip);
return(err_code);
}
if ( (ip = last_dir(user_path, string)) == NIL_INODE) r = err_code;
/* If 'name2' exists in full (even if no space) set 'r' to error. */
if (r == OK) {
if ( (new_ip = advance(ip, string)) == NIL_INODE) {
r = err_code;
if (r == ENOENT) r = OK;
} else {
put_inode(new_ip);
r = EEXIST;
}
}
/* Check for links across devices. */
if (r == OK)
if (rip->i_dev != ip->i_dev) r = EXDEV;
/* Try to link. */
if (r == OK)
r = search_dir(ip, string, &rip->i_num, ENTER);
/* If success, register the linking. */
if (r == OK) {
rip->i_nlinks++;
rip->i_dirt = DIRTY;
}
/* Done. Release both inodes. */
put_inode(rip);
put_inode(ip);
return(r);
}
/*===========================================================================*
* do_unlink *
*===========================================================================*/
PUBLIC int do_unlink()
{
/* Perform the unlink(name) or rmdir(name) system call. The code for these two
* is almost the same. They differ only in some condition testing. Unlink()
* may be used by the superuser to do dangerous things; rmdir() may not.
*/
register struct inode *rip;
struct inode *rldirp;
register struct fproc *rfp;
int r, r1;
ino_t numb;
mode_t old_mode;
uid_t old_uid;
char string[NAME_MAX];
/* Get the last directory in the path. */
if (fetch_name(name, name_length, M3) != OK) return(err_code);
if ( (rldirp = last_dir(user_path, string)) == NIL_INODE)
return(err_code);
/* The last directory exists. Does the file also exist? */
r = OK;
if ( (rip = advance(rldirp, string)) == NIL_INODE) r = err_code;
/* If error, return inode. */
if (r != OK) {
put_inode(rldirp);
return(r);
}
old_mode = rip->i_mode; /* save mode; it must be fudged for . and .. */
old_uid = rip->i_uid; /* save uid; it must be fudged for . and .. */
/* Now test if the call is allowed, separately for unlink() and rmdir(). */
if (fs_call == UNLINK) {
/* Only the su may unlink directories, but the su can unlink any dir.*/
if ( (rip->i_mode & I_TYPE) == I_DIRECTORY && !super_user) r = EPERM;
/* Actually try to unlink the file; fails if parent is mode 0 etc. */
if (r == OK) r = search_dir(rldirp, string, (ino_t *) 0, DELETE);
} else {
/* The call is rmdir(). Five conditions have to met for this call:
* - The file must be a directory
* - The directory must be empty (except for . and ..)
* - It must not be /
* - The path must not end in . or ..
* - The directory must not be anybody's working directory
*/
if ( (rip->i_mode & I_TYPE) != I_DIRECTORY) r = ENOTDIR;
if (search_dir(rip, "", &numb, LOOK_UP) == OK) r = ENOTEMPTY;
if (strcmp(user_path, "/") == 0) r = EPERM; /* can't remove root */
if (strcmp(string, ".") == 0 || strcmp(string, "..") == 0) r = EPERM;
for (rfp = &fproc[INIT_PROC_NR + 1]; rfp < &fproc[NR_PROCS]; rfp++) {
if (rfp->fp_workdir == rip || rfp->fp_rootdir == rip) {
r = EBUSY; /* can't remove anybody's working dir*/
break;
}
}
/* Actually try to unlink the file; fails if parent is mode 0 etc. */
if (r == OK) r = search_dir(rldirp, string, (ino_t *) 0, DELETE);
/* If all the conditions have been met, remove . and .. from the dir.
* If the directory is not searchable, it will not be possible to
* unlink . and .. even though this is legal, so change the mode.
*/
if (r == OK) {
rip->i_mode |= S_IRWXU; /* turn on all the owner bits */
rip->i_uid = fp->fp_effuid; /* may not fail due to uid */
if ( (r = search_dir(rip, ".", (ino_t *) 0, DELETE)) == OK)
rip->i_nlinks --; /* . pts to dir being removed*/
if ( (r1 = search_dir(rip, "..", (ino_t *) 0, DELETE)) == OK)
rldirp->i_nlinks--; /* .. points to parent dir */
rip->i_dirt = DIRTY;
rldirp->i_dirt = DIRTY;
if (r1 != OK) r = r1;
rip->i_mode = old_mode; /* restore the old mode */
rip->i_uid = old_uid;
}
}
if (r == OK) {
rip->i_nlinks--;
rip->i_dirt = DIRTY;
}
/* If unlink was possible, it has been done, otherwise it has not. */
rip->i_mode = old_mode; /* restore mode in case it has been changed */
put_inode(rip);
put_inode(rldirp);
return(r);
}
/*===========================================================================*
* do_rename *
*===========================================================================*/
PUBLIC int do_rename()
{
/* Perform the rename(name1, name2) system call. */
struct inode *old_dirp, *old_ip; /* ptrs to old dir, file inodes */
struct inode *new_dirp, *new_ip; /* ptrs to new dir, file inodes */
int r = OK; /* error flag; initially no error */
int odir, ndir; /* TRUE iff {old|new} file is dir */
char string[NAME_MAX+1], old_string[NAME_MAX+1];
char old_name[PATH_MAX+1];
ino_t numb;
/* See if 'name1' (existing file) exists. Get dir and file inodes. */
if (fetch_name(name1, name1_length, M1) != OK) return(err_code);
if ( (old_dirp = last_dir(user_path, string)) == NIL_INODE) return(err_code);
if ( (old_ip = advance(old_dirp, string)) == NIL_INODE) r = err_code;
strcpy(old_name, user_path); /* save the old name here */
strcpy(old_string, string); /* save last component of the name here */
/* See if 'name2' (new name) exists. Get dir and file inodes. */
if (fetch_name(name2, name2_length, M1) != OK) r = err_code;
if ( (new_dirp = last_dir(user_path, string)) == NIL_INODE) r = err_code;
new_ip = advance(new_dirp, string); /* not required to exist */
/* If it is ok, check for a variety of possible errors. */
if (r == OK) {
/* The old path must not be a prefix of the new one. */
if (strncmp(old_name, user_path, strlen(old_name)) == 0) r = EINVAL;
/* The old path must not be . or .. */
if (strcmp(old_name, ".")==0 || strcmp(old_name, "..")==0) r = EINVAL;
/* Both directories must be on the same device. */
if (old_dirp->i_dev != new_dirp->i_dev) r = EXDEV;
/* Both directories must be writable and searchable. */
if (forbidden(old_dirp, W_BIT | X_BIT, 0)) r = EACCES;
if (forbidden(new_dirp, W_BIT | X_BIT, 0)) r = EACCES;
/* Some tests apply only if the new path exists. */
odir = S_ISDIR(old_ip->i_mode); /* TRUE iff old file is dir */
if (new_ip != NIL_INODE) {
ndir = S_ISDIR(new_ip->i_mode); /* TRUE iff new file is dir */
if (odir == TRUE && ndir == FALSE) r = ENOTDIR;
if (odir == FALSE && ndir == TRUE) r = EISDIR;
if (old_ip->i_num == new_ip->i_num)